package com.zhihao.zhihaopicturebackend.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhihao.zhihaopicturebackend.constant.UserConstant;
import com.zhihao.zhihaopicturebackend.exception.BusinessException;
import com.zhihao.zhihaopicturebackend.exception.ErrorCode;
import com.zhihao.zhihaopicturebackend.exception.ThrowUtils;
import com.zhihao.zhihaopicturebackend.manager.auth.StpKit;
import com.zhihao.zhihaopicturebackend.mapper.SystemUserMapper;
import com.zhihao.zhihaopicturebackend.model.dto.user.UserLoginRequest;
import com.zhihao.zhihaopicturebackend.model.dto.user.UserPasswordModifyRequest;
import com.zhihao.zhihaopicturebackend.model.dto.user.UserQueryRequest;
import com.zhihao.zhihaopicturebackend.model.dto.user.UserRegisterRequest;
import com.zhihao.zhihaopicturebackend.model.entity.SystemUser;
import com.zhihao.zhihaopicturebackend.model.enums.UserRoleEnum;
import com.zhihao.zhihaopicturebackend.model.vo.LoginUserVO;
import com.zhihao.zhihaopicturebackend.model.vo.UserVO;
import com.zhihao.zhihaopicturebackend.service.SystemUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.zhihao.zhihaopicturebackend.constant.UserConstant.USER_LOGIN_STATE;

/**
 * @author Jackson
 * @description 针对表【system_user(用户表)】的数据库操作Service实现
 * @createDate 2024-12-20 15:17:37
 */
@Slf4j
@Service
public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemUser>
        implements SystemUserService {

    @Override
    public Long userRegister(UserRegisterRequest userRegisterRequest) {
        // 1. 两次密码校验(使用spring-boot-validation组件是声明注解式开发实现大部分JSR303参数校验)
        if (!userRegisterRequest.getUserPassword().equals(userRegisterRequest.getCheckPassword())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入密码不一致,请重试!");
        }
        // 2. 数据库是否已有重复记录检查
        QueryWrapper<SystemUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_account", userRegisterRequest.getUserAccount());
        Long count = this.baseMapper.selectCount(queryWrapper);
        ThrowUtils.throwIf(count > 0, ErrorCode.PARAMS_ERROR, "账号已存在,请重试!");
        // 3. 密码加密
        String encryptPassword = getEncryptPassword(userRegisterRequest.getUserPassword());
        // 4. 插入数据
        String initialUserName = generateUniqueUserName();  // 初始化唯一用户名
        SystemUser systemUser = new SystemUser().setUserAccount(userRegisterRequest.getUserAccount())
                .setUserPassword(encryptPassword)
                .setUserName(initialUserName)
                .setUserRole(UserRoleEnum.USER.getValue());
        boolean saveResult = this.save(systemUser);
        ThrowUtils.throwIf(!saveResult,ErrorCode.SYSTEM_ERROR,"系统错误,注册失败!");
        // 5. 返回注册后的用户id
        return systemUser.getId();
    }

    @Override
    public LoginUserVO userLogin(UserLoginRequest userLoginRequest, HttpServletRequest request) {
        // 1. 加密 (密文对密文)
        String encryptPassword = getEncryptPassword(userLoginRequest.getUserPassword());
        // 查询用户是否存在
        QueryWrapper<SystemUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_account", userLoginRequest.getUserAccount());
        queryWrapper.eq("user_password", encryptPassword);
        SystemUser user = this.baseMapper.selectOne(queryWrapper);
        // 用户不存在
        if (ObjectUtil.isNull(user)) {
            log.info("user login failed, user_account cannot match user_password");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        // 2.1 本地SpringSession记录用户的登录态
        request.getSession().setAttribute(USER_LOGIN_STATE, user);
        // 2.2 sa-token记录用户登录态，便于空间鉴权时使用
        //      注意保证该用户信息与 SpringSession 中的信息过期时间一致(因为本质上空间鉴权是应该基于用户登录态的基础上的鉴权)
        StpKit.SPACE.login(user.getId());   // 产生一个新的会话session
        StpKit.SPACE.getSession().set(UserConstant.USER_LOGIN_STATE, user);
        // 3. 数据脱敏
        LoginUserVO loginUserVO = BeanUtil.copyProperties(user, LoginUserVO.class);
        return loginUserVO;
    }

    @Override
    public SystemUser getLoginUser(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        SystemUser currentUser = (SystemUser) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        // 从数据库查询（追求性能的话可以注释，直接返回上述结果）
        //      如果这样,就没必要从Session中获取了啊
        //      (Session中的数据不一定正确，万一用户登录期间更新用户信息了，update代码中不更新Session，岂不是不够实时吗？---缓存没更新)
        // 综上:感觉没必要从Session中去取，直接主键索引id查性能又不差
        long userId = currentUser.getId();
        currentUser = this.getById(userId);
        if (currentUser == null) {  // 用户登录过程中账号意外被封禁/删除
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        return currentUser;
    }

    @Override
    public boolean userLogout(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        if (userObj == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        // 移除登录态
        request.getSession().removeAttribute(USER_LOGIN_STATE);
        return true;
    }

    @Override
    public UserVO getUserVO(SystemUser user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @Override
    public List<UserVO> getUserVOList(List<SystemUser> userList) {
        if (CollUtil.isEmpty(userList)) {
            return new ArrayList<>();
        }
        // stream()流式编程+lambda表达式
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }

    @Override
    public boolean isAdmin(SystemUser user) {
        return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }


    @Override
    public QueryWrapper<SystemUser> getQueryWrapper(UserQueryRequest userQueryRequest) {
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();
        String userAccount = userQueryRequest.getUserAccount();
        String userName = userQueryRequest.getUserName();
        String userProfile = userQueryRequest.getUserProfile();
        String userRole = userQueryRequest.getUserRole();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();
        QueryWrapper<SystemUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(ObjectUtil.isNotNull(id), "id", id);
        queryWrapper.eq(StrUtil.isNotBlank(userRole), "user_role", userRole);
        queryWrapper.like(StrUtil.isNotBlank(userAccount), "user_account", userAccount);
        queryWrapper.like(StrUtil.isNotBlank(userName), "user_name", userName);
        queryWrapper.like(StrUtil.isNotBlank(userProfile), "user_profile", userProfile);
        queryWrapper.orderBy(StrUtil.isNotEmpty(sortField), sortOrder.equals("ascend"), sortField);
        return queryWrapper;
    }



    @Override
    public String getEncryptPassword(String userPassword) {
        // salt-盐值,混淆密码
        final String SALT = "zhihao";
        return DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
    }

    @Override
    public Boolean modifyUserPassword(UserPasswordModifyRequest userPasswordModifyRequest, HttpServletRequest request) {
        // 1. 获取用户信息(主要是用户id-不可变的)
        SystemUser user = (SystemUser) request.getSession().getAttribute(USER_LOGIN_STATE);
        Long userId = user.getId();
        // 2. 获取最新的用户信息
        LambdaQueryWrapper<SystemUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SystemUser::getId, userId);
        SystemUser currentUserInfo = this.baseMapper.selectOne(queryWrapper);
        // 3. 两次密码是否一致
        if (!userPasswordModifyRequest.getNewPassword().equals(userPasswordModifyRequest.getCheckNewPassword())){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"两次新密码不一致,请重试!");
        }
        // 4. 校验原密码是否输入正确
        String inputPassword = getEncryptPassword(userPasswordModifyRequest.getUserPassword());
        if (!currentUserInfo.getUserPassword().equals(inputPassword)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"原密码错误,请重试!");
        }
        // 6. 新密码加密
        String newPassword = getEncryptPassword(userPasswordModifyRequest.getNewPassword());
        // 5. 更新
        SystemUser updateUser = new SystemUser();
        updateUser.setId(userId).setUserPassword(newPassword);
        boolean result = this.updateById(updateUser);
        return result;
    }

    private String generateUniqueUserName() {
        String userName;
        QueryWrapper<SystemUser> queryWrapper = new QueryWrapper<>();
        do {
            // 生成基于‘固定字符串+时间戳’的用户名
            String timeStamp = String.valueOf(System.currentTimeMillis());
            userName = "user_" + timeStamp;
            // 检查用户名是否已存在
            queryWrapper.eq("user_name", userName);
        } while (this.baseMapper.selectCount(queryWrapper) > 0);
        return userName;
    }

}




